package cn.tedu.mall.order.service.impl;

import cn.tedu.mall.common.exception.CoolSharkServiceException;
import cn.tedu.mall.common.pojo.domain.CsmallAuthenticationInfo;
import cn.tedu.mall.common.restful.JsonPage;
import cn.tedu.mall.common.restful.ResponseCode;
import cn.tedu.mall.order.mapper.OmsOrderItemMapper;
import cn.tedu.mall.order.mapper.OmsOrderMapper;
import cn.tedu.mall.order.service.IOmsCartService;
import cn.tedu.mall.order.service.IOmsOrderService;
import cn.tedu.mall.order.utils.IdGeneratorUtils;
import cn.tedu.mall.pojo.order.dto.OrderAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderItemAddDTO;
import cn.tedu.mall.pojo.order.dto.OrderListTimeDTO;
import cn.tedu.mall.pojo.order.dto.OrderStateUpdateDTO;
import cn.tedu.mall.pojo.order.model.OmsCart;
import cn.tedu.mall.pojo.order.model.OmsOrder;
import cn.tedu.mall.pojo.order.model.OmsOrderItem;
import cn.tedu.mall.pojo.order.vo.OrderAddVO;
import cn.tedu.mall.pojo.order.vo.OrderDetailVO;
import cn.tedu.mall.pojo.order.vo.OrderListVO;
import cn.tedu.mall.product.service.order.IForOrderSkuService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

@DubboService //将来要被seckill模块调用
@Service
@Slf4j
public class OmsOrderServiceImpl implements IOmsOrderService {
    //远程调用Product模块减少sku库存
    @DubboReference
    private IForOrderSkuService dubboOrderSkuService;
    //删除购物车中选中的商品
    @Autowired
    private IOmsCartService omsCartService;
    //新增订单
    @Autowired
    private OmsOrderMapper omsOrderMapper;
    //新增订单项
    @Autowired
    private OmsOrderItemMapper omsOrderItemMapper;

    //涉及到了多个数据源，需要保证分布式事务，由于该方法会触发其他数据源的操作，是一个事务的起点，在这里开启事务
    @GlobalTransactional
    @Override
    public OrderAddVO addOrder(OrderAddDTO orderAddDTO) {
        //第一部分：收集订单信息，准备数据
        OmsOrder omsOrder = new OmsOrder();
        //将orderAddDTO中的同名属性赋值到omsOrder对象中
        BeanUtils.copyProperties(orderAddDTO,omsOrder);
        //收集信息和计算，单独写成一个方法
        loadOrder(omsOrder);
        //接下来是订单项的信息，获取这些订单项，判断是否为null，进行赋值
        List<OrderItemAddDTO> orderItemAddDTOS = orderAddDTO.getOrderItems();
        //判断订单项是否为null或为空，是直接抛出异常
        if(orderItemAddDTOS==null || orderItemAddDTOS.isEmpty()){
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"订单中必须保护订单项");
        }
        //目前是List<OrderItemAddDTO>类型，但是持久层需要的是List<OmsOrderItem>，需要转成
        List<OmsOrderItem> omsOrderItems = new ArrayList<>();
        //遍历orderItemAddDTOS，进行转成
        for (OrderItemAddDTO orderItemAddDTO : orderItemAddDTOS) {
            OmsOrderItem omsOrderItem = new OmsOrderItem();
            BeanUtils.copyProperties(orderItemAddDTO,omsOrderItem);
            //接下来需要判断omsOrderItem对象中的属性，也写成一个单独的方法进行判断
            loadOrderItem(omsOrderItem);
            //将订单id赋值给omsOrderItem
            omsOrderItem.setOrderId(omsOrder.getId());
            //添加到omsOrderItems集合中
            omsOrderItems.add(omsOrderItem);
            //到此订单信息的收集和计算完成了
            //第二部分：完成数据库操作的过程
            //2.1减少skuId的库存
            int rows = dubboOrderSkuService.reduceStockNum(omsOrderItem.getSkuId(), omsOrderItem.getQuantity());
            //如果rows==0，就是减少库存失败
            if(rows==0){
                log.info("商品skuId为{}，库存不足",omsOrderItem.getSkuId());
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"库存不足");
            }
            //2.2 删除购物车中选中的商品
            OmsCart omsCart = new OmsCart();
            omsCart.setUserId(omsOrder.getUserId());
            omsCart.setSkuId(omsOrderItem.getSkuId());
            omsCartService.removeUserCarts(omsCart);
        }
        //2.3新增订单
        omsOrderMapper.insertOrder(omsOrder);
        //2.4新增订单项
        omsOrderItemMapper.insertOrderItem(omsOrderItems);
        //程序设计返回值类型是OrderAddVO类型
        OrderAddVO orderAddVO = new OrderAddVO();
        orderAddVO.setId(omsOrder.getId());
        orderAddVO.setSn(omsOrder.getSn());
        orderAddVO.setCreateTime(omsOrder.getGmtCreate());
        orderAddVO.setPayAmount(omsOrder.getAmountOfActualPay());
        return orderAddVO;
    }

    private void loadOrderItem(OmsOrderItem omsOrderItem) {
        if(omsOrderItem.getId()==null){
            Long id = IdGeneratorUtils.getDistributeId("order_item");
            omsOrderItem.setId(id);
        }
        if(omsOrderItem.getSkuId()==null){
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"订单商品中必须包含skuId");
        }
    }

    //新增订单中，要收集和计算order的对象信息
    private void loadOrder(OmsOrder omsOrder) {
        //针对omsOrder对象中必须具备但是为null的值进行赋值
        //1.判断id是否为null
        if(omsOrder.getId()==null){
            //利用Leaf生成分布式ID进行赋值
            Long id = IdGeneratorUtils.getDistributeId("order");
            omsOrder.setId(id);
        }
        //2.判断sn是否为null
        if(omsOrder.getSn()==null){
            String sn = UUID.randomUUID().toString();
            omsOrder.setSn(sn);
        }
        //3.判断用户id是否为null
        if(omsOrder.getUserId()==null){
            Long userId = getUserId();
            omsOrder.setUserId(userId);
        }
        //4.判断state是否为null
        if(omsOrder.getState()==null){
            //订单默认状态是0
            omsOrder.setState(0);
        }
        //5.为了保证生成订单时间和数据创建时间以及最后修改时间的一致性
        //本次新增订单使用手动赋予统一的值
        if(omsOrder.getGmtOrder()==null){
            LocalDateTime now = LocalDateTime.now();
            omsOrder.setGmtOrder(now);
            omsOrder.setGmtCreate(now);
            omsOrder.setGmtModified(now);
        }
        //6.商品原价为null，直接抛出异常
        if(omsOrder.getAmountOfOriginalPrice()==null){
            throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"商品没有原价！");
        }
        //7.判断运费是否为null
        if(omsOrder.getAmountOfFreight()==null){
            //转成数值类型参与计算
            omsOrder.setAmountOfFreight(new BigDecimal(0.0));
        }
        //8.判断优惠价格是否为null
        if(omsOrder.getAmountOfDiscount()==null){
            //转成数值类型参与计算
            omsOrder.setAmountOfDiscount(new BigDecimal(0.0));
        }
        //9.计算实际支付金额
        //实际支付金额=商品原价+运费-优惠价格
        BigDecimal amountOfOriginalPrice = omsOrder.getAmountOfOriginalPrice();
        BigDecimal amountOfFreight = omsOrder.getAmountOfFreight();
        BigDecimal amountOfDiscount = omsOrder.getAmountOfDiscount();
        BigDecimal actualPrice = amountOfOriginalPrice.add(amountOfFreight).subtract(amountOfDiscount);
        //给实际金额进行赋值
        omsOrder.setAmountOfActualPay(actualPrice);
    }

    //根据订单id修改订单状态
    @Override
    public void updateOrderState(OrderStateUpdateDTO orderStateUpdateDTO) {
        OmsOrder omsOrder = new OmsOrder();
        BeanUtils.copyProperties(orderStateUpdateDTO,omsOrder);
        omsOrderMapper.updateOrderById(omsOrder);
    }

    //按指定时间进行分页查询订单信息
    @Override
    public JsonPage<OrderListVO> listOrdersBetweenTimes(OrderListTimeDTO orderListTimeDTO) {
        //是否指定时间，如果没有指定时间，添加默认时间(最近一个月)
        //如果有时间需要去判断是否合理(startTime<endTime)
        validaTimeAndLoadTimes(orderListTimeDTO);
        //获取用户id，进行赋值
        Long userId = getUserId();
        orderListTimeDTO.setUserId(userId);
        //设置分页条件
        PageHelper.startPage(orderListTimeDTO.getPage(),orderListTimeDTO.getPageSize());
        //执行查询
        List<OrderListVO> orderListVOS = omsOrderMapper.selectOrdersByTime(orderListTimeDTO);
        return JsonPage.restPage(new PageInfo<>(orderListVOS));
    }

    private void validaTimeAndLoadTimes(OrderListTimeDTO orderListTimeDTO) {
        //取出时间，只要startTime和endTime有一个为null，就赋予默认时间
        LocalDateTime startTime = orderListTimeDTO.getStartTime();
        LocalDateTime endTime = orderListTimeDTO.getEndTime();
        if(startTime==null || endTime==null){
            startTime = LocalDateTime.now().minusMonths(1);
            endTime = LocalDateTime.now();
            orderListTimeDTO.setStartTime(startTime);
            orderListTimeDTO.setEndTime(endTime);
        } else {
            //校验时间是否合理
            if(endTime.toInstant(ZoneOffset.of("+8")).toEpochMilli()
                    <startTime.toInstant(ZoneOffset.of("+8")).toEpochMilli()){
                //结束时间小于开始时间，抛出异常
                throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"结束时间小于开始时间，格式不正确!");
            }
        }
    }

    @Override
    public OrderDetailVO getOrderDetail(Long id) {
        return null;
    }
    //获取用户信息
    public CsmallAuthenticationInfo getUserInfo(){
        //获取SpringSecurity上下文对象
        UsernamePasswordAuthenticationToken token =
                (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        //判断token是否为空
        if(token==null)
            throw new CoolSharkServiceException(ResponseCode.UNAUTHORIZED,"没有登录信息");
        //token不为空，获取用户信息
        CsmallAuthenticationInfo userInfo =
                (CsmallAuthenticationInfo) token.getCredentials();
        //返回登录用户的信息
        return userInfo;
    }
    //获取用户id
    public Long getUserId(){
        return getUserInfo().getId();
    }
}
